[新機能] Amazon CloudFrontがCORSに対応しました
CloudFrontに大量アップデートがやってきました。このエントリーではCORSに関して解説します。 Amazon CloudFront Adds Device Detection, Geo Targeting, Host Header Forwarding, CORS Support, and more!
これまでの課題
これまでもCloudFrontはOriginのレスポンスにAccess-Control-Allow-Originヘッダが含まれる場合はキャッシュしてくれました。ただし、ブラウザのドメインとCloudFrontのドメインが一致する場合はCORSにする必要が無いためブラウザはOriginヘッダをリクエストに含めません。結果としてレスポンスにAccess-Control-Allow-Originヘッダが含まれません。そうすると、最初にCloudFrontにアクセスするクライアントが同一ドメインの場合はAccess-Control-Allow-Originヘッダがキャッシュされず、その後他のドメインからOriginヘッダを含めてリクエストを投げてもCloudFrontのキャッシュにはAccess-Control-Allow-Originヘッダが存在しないためエラーとなっていました。
最初のアクセス | Access-Control-Allow-Originヘッダ | 次回以降がCORSの場合 |
同一ドメイン | キャッシュされない | エラーとなる |
ドメインが一致しない(CORS) | キャッシュされる | 動作する |
特に問題となるのがS3の場合でした。EC2インスタンス上のWebアプリケーションであればOriginヘッダの有無に関係なくAccess-Control-Allow-Originヘッダを返すこともできますが、S3のCORS機能は仕様通りの実装であるためOriginヘッダが存在しない場合はAccess-Control-Allow-Originヘッダを返しません。具体的には、以下の様な挙動になります。まずはOriginヘッダをつけない(同一ドメイン)の場合です。curlコマンドでStatic Website Hostingを有効にしたS3バケットhoge.example.com.s3-website-ap-1.amazonaws.comに対してアクセスします。
$ curl -I hoge.example.com.s3-website-ap-1.amazonaws.com/hoge HTTP/1.1 200 OK x-amz-id-2: M6gdDKXWgtxdJN+gB3Eiy6eYMMqYGh10YU/Rop8GtxWUDve1fyqEZxMVDJlcC1cj x-amz-request-id: C886951A05948413 Date: Sat, 28 Jun 2014 09:04:00 GMT Last-Modified: Sat, 28 Jun 2014 09:03:34 GMT x-amz-expiration: expiry-date="Mon, 30 Jun 2014 00:00:00 GMT", rule-id="Rule for the Entire Bucket" ETag: "d41d8cd98f00b204e9800998ecf8427e" Content-Type: application/octet-stream Content-Length: 0 Server: AmazonS3 Connection: keep-alive
普通にレスポンスが返ってきますね。では次にCORSとなるように-HオプションでOriginヘッダをつけてみます。
$ curl -I -H "Origin: http://fuga.example.com" hoge.example.com.s3-website-ap-northeast-1.amazonaws.com/hoge HTTP/1.1 200 OK x-amz-id-2: 5UQ/FD+mJlLGadzQTLqnMSbi+dtgPv4jvwjNxaF41stdvqWC1sAFfBXEIPnWDDHF x-amz-request-id: E778FEF9A73981C4 Date: Sat, 28 Jun 2014 09:05:23 GMT Access-Control-Allow-Origin: * Access-Control-Allow-Methods: HEAD Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method Last-Modified: Sat, 28 Jun 2014 09:03:34 GMT x-amz-expiration: expiry-date="Mon, 30 Jun 2014 00:00:00 GMT", rule-id="Rule for the Entire Bucket" ETag: "d41d8cd98f00b204e9800998ecf8427e" Content-Type: application/octet-stream Content-Length: 0 Server: AmazonS3 Connection: keep-alive
Access-Control-Allow-Originヘッダ、Access-Control-Allow-Methodsヘッダ、Varyヘッダが追加されていることがわかると思います。
前述のS3にアクセスした場合の挙動を押さえた上で、今度はCloudFront経由でS3にアクセスしてみたいと思います。ここではCORS設定を行っていないCloudFrontとしてcors.disabled.cloudfront.netを用意した前提で先ほどと同じようにcurlコマンドでアクセスしてみます。
$ curl -I cors.disabled.cloudfront.net/hoge HTTP/1.1 200 OK Content-Type: application/octet-stream Content-Length: 0 Date: Sat, 28 Jun 2014 09:49:46 GMT Last-Modified: Sat, 28 Jun 2014 09:03:34 GMT x-amz-expiration: expiry-date="Mon, 30 Jun 2014 00:00:00 GMT", rule-id="Rule for the Entire Bucket" ETag: "d41d8cd98f00b204e9800998ecf8427e" Accept-Ranges: bytes Server: AmazonS3 X-Cache: Miss from cloudfront Via: 1.1 ce5b2b9206bda1204c308b238ba5622a.cloudfront.net (CloudFront) X-Amz-Cf-Id: zahIdNc5_2t7dbPGEGBt0evQ4eHhUlBKeXcQpr2nZEnkUwvRnhN0nw== Connection: keep-alive
S3の場合と同様に普通にレスポンスが返ってきますね。では次にCORSとなるように-HオプションでOriginヘッダをつけてみます。
$ curl -I -H "Origin: http://fuga.example.com" cors.disabled.cloudfront.net/hoge HTTP/1.1 200 OK Content-Type: application/octet-stream Content-Length: 0 Date: Sat, 28 Jun 2014 09:49:46 GMT Last-Modified: Sat, 28 Jun 2014 09:03:34 GMT x-amz-expiration: expiry-date="Mon, 30 Jun 2014 00:00:00 GMT", rule-id="Rule for the Entire Bucket" ETag: "d41d8cd98f00b204e9800998ecf8427e" Accept-Ranges: bytes Server: AmazonS3 Age: 8 X-Cache: Hit from cloudfront Via: 1.1 ee0dd13be5688393446d3eadfad27909.cloudfront.net (CloudFront) X-Amz-Cf-Id: pIcp6BnKDPJYRRAwhyljcB8YOweqoU8FxInvxiuoMG4SFByp6wK_1Q== Connection: keep-alive
S3の場合とは異なり、Access-Control-Allow-Originヘッダなどが返されません。理由としてはX-CacheヘッダにあるようにCloudFrontがキャッシュを返しているためです。
新機能として追加されたこと
OriginヘッダをOriginサーバー側に転送できるようになり、その際にOriginヘッダ毎にレスポンスをキャッシュしてくれるようになりました。この結果、同一ドメインの場合はAccess-Control-Allow-Originヘッダなしのレスポンスがキャッシュされ、ドメインが一致しないCORS対象となる場合はAccess-Control-Allow-Originヘッダ付きのレスポンスがキャッシュされるようになりました。
以下にS3オリジンの場合のマネジメントコンソールのスクリーンショットを貼ります。CloudFrontを作成する際に[Forward Headers]を[Whitelist]に変更し、[Whitelist Headers]でOriginヘッダを[Add>>]ボタンで追加します。
動作確認
では実際に[Forward Headers]を設定することで挙動が変化したのか確認したいと思います。今度は[Forward Headers]を設定したcors.enabled.cloudfront.netというCloudFrontを用意した前提で先ほどと同じ手順について確認します。
$ curl -I cors.enabled.cloudfront.net/hoge HTTP/1.1 200 OK Content-Type: application/octet-stream Content-Length: 0 Date: Sat, 28 Jun 2014 11:16:08 GMT Last-Modified: Sat, 28 Jun 2014 09:03:34 GMT x-amz-expiration: expiry-date="Mon, 30 Jun 2014 00:00:00 GMT", rule-id="Rule for the Entire Bucket" ETag: "d41d8cd98f00b204e9800998ecf8427e" Accept-Ranges: bytes Server: AmazonS3 X-Cache: Miss from cloudfront Via: 1.1 ee0dd13be5688393446d3eadfad27909.cloudfront.net (CloudFront) X-Amz-Cf-Id: UlkEH-6PiMyTC42Kzq-EBhPLeefBvvDnJvnEMUjhg7ULR3119UMzsg== Connection: keep-alive
S3の場合と同様に普通にレスポンスが返ってきますね。では次にCORSとなるように-HオプションでOriginヘッダをつけてみます。
$ curl -I -H "Origin: http://fuga.example.com" cors.enabled.cloudfront.net/hoge HTTP/1.1 200 OK Content-Type: application/octet-stream Content-Length: 0 Date: Sat, 28 Jun 2014 11:15:53 GMT Access-Control-Allow-Origin: * Access-Control-Allow-Methods: HEAD Last-Modified: Sat, 28 Jun 2014 09:03:34 GMT x-amz-expiration: expiry-date="Mon, 30 Jun 2014 00:00:00 GMT", rule-id="Rule for the Entire Bucket" ETag: "d41d8cd98f00b204e9800998ecf8427e" Accept-Ranges: bytes Server: AmazonS3 Vary: Origin X-Cache: Miss from cloudfront Via: 1.1 b291b21c612e764f4bf23bc28c9e37f5.cloudfront.net (CloudFront) X-Amz-Cf-Id: hA9qseHsYyHqklRcShNRdYFuU_MO-Gs6kLQYDqYRTdzKkYC3wmZeTg== Connection: keep-alive
S3の場合と同様にAccess-Control-Allow-Originヘッダなどが返ってきていますね!
まとめ
これでやっとCloudFront+S3でCORSに対応することが出来ます。S3についてはだいぶ前からCORS対応していたのですが、CloudFrontと組み合わせた際に問題となるケースがあったので待ち望んでいた機能でした。
今回のCloudFrontの機能追加はリクエストヘッダをKeyとしてキャッシュを制御するという非常にシンプルなものですが、応用範囲が広いことについて驚きました。詳細はアナウンスページからもリンクがありますが、以下のページをご参照下さい。 Configuring CloudFront to Cache Objects Based on Request Headers - Amazon CloudFront